<?php

/**
 * Alkalmazások ősosztálya
 *
 * @author prometheus
 */
abstract class Task_Application extends Task
{
	/**
	 * Nincs meghatározott HTTP hibakód (korábban beállított hibakóddal térhet
	 * vissza a kiszolgáló a kliens felé).
	 */
	const HTTP_NO_ERROR = FALSE;

	const HTTP_CONTINUE = 100;
	const HTTP_SWITCHING_PROTOCOLS = 101;

	/**
	 * Minden rendben.
	 */
	const HTTP_OK = 200;

	/**
	 * A felhasználó tartalma rendben létrejött.
	 * Példa: "a hozzászólás létrejött, itt és itt megtekintheted" - tájékoztatás.
	 * Javaslat: Opcionálisan Location mező használatával a látogató átirányítható
	 * a megfelelő (preferált) címre.
	 */
	const HTTP_CREATED = 201;

	/**
	 * Kérés fogadva - feldolgozása később történik meg.
	 * Példa: ütemezett feladatot kezdeményezett a felhasználó.
	 */
	const HTTP_ACCEPTED = 202;
	const HTTP_NON_AUTHORITATIVE_INFO = 203;

	/**
	 * A kérés fogadva - a visszatérő fejléceket a kliensnek kell frissítenie,
	 * tartalom nem tért vissza.
	 */
	const HTTP_NO_CONTENT = 204;
	const HTTP_RESET_CONTENT = 205;
	const HTTP_PARTIAL_CONTENT = 206;

	/**
	 * A keresett erőforrás többször fordul elő más címen, más formátumban.
	 * Példa: egy listaoldal, amelynek létezik HTML és RSS megvalósítása is, vagy
	 * egy oldal, amely elérhető több más nyelven is (de a nyelv nincs meghatározva
	 * az erőforrás lekérésében).
	 * Javaslat: Location mező megadásával a klienst egy preferált útvonalra
	 * kell irányítani ilyenkor.
	 */
	const HTTP_MULTIPLE_CHOICES = 300;

	/**
	 * A keresett erőforrás helye véglegesen megváltozott egy másikra.
	 * Példa: egy oldal címe (vagy álneve) meg lett változtatva.
	 * Javaslat: A Location mező megadásával a klienst át kell irányítani az
	 * új útvonalra.
	 */
	const HTTP_MOVED_PERMANENTLY = 301;

	/**
	 * A keresett erőforrás átmenetileg (pillanatnyilag) egy másik címen található.
	 * Példa: ?
	 * Javaslat: A Location mező megadásával a klienst át kell irányítani az
	 * új útvonalra.
	 */
	const HTTP_FOUND = 302;

	/**
	 * A POST-tal igényelt erőforrás kimenete egy másik címről GET-tel érhető el.
	 * Példa: ?
	 * Javaslat: A Location mező megadásával a klienst át kell irányítani az
	 * új útvonalra.
	 */
	const HTTP_SEE_OTHER = 303;
	const HTTP_NOT_MODIFIED = 304;
	const HTTP_USE_PROXY = 305;
	const HTTP_TEMPORARY_REDIRECT = 307;
	const HTTP_BAD_REQUEST = 400;

	/**
	 * Az erőforrás eléréséhez bejelentkezés szükséges.
	 * Példa: védett tartalmat megtekinteni óhajtó látogató esete.
	 * Javaslat: A Location mező megadásával a klienst irányítsuk át a beléptető
	 * oldalra.
	 */
	const HTTP_UNAUTHORIZED = 401;
	const HTTP_PAYMENT_REQUIRED = 402;

	/**
	 * A keresett erőforráshoz a belépett felhasználónak nincs jogosultsága.
	 * Példa: egyszeri felhasználó nem férhet hozzá az adminisztrációs felülethez.
	 * Javaslat: A látogatót tájékoztatni, visszairányítani a korábban
	 * megtekintett oldalra (referrer segítségével, pl.).
	 */
	const HTTP_FORBIDDEN = 403;

	/**
	 * A keresett erőforrás nem található.
	 * Példa: -
	 * Javaslat: Használható olyan esetekben is, amikor kiemelten érzékeny
	 * erőforrást akarunk leplezni jogosulatlan felhasználók előtt.
	 */
	const HTTP_NOT_FOUND = 404;
	const HTTP_METHOD_NOT_ALLOWED = 405;
	const HTTP_NOT_ACCEPTABLE = 406;
	const HTTP_PROXY_AUTH_REQUIRED = 407;
	const HTTP_REQUEST_TIMEOUT = 408;

	/**
	 * A kérés nem hajtható végre, mivel az ütközik az erőforrás jelenlegi
	 * állapotával.
	 * Példa: egy verziókövető alkalmazás, ahol időközben módosult a szóban
	 * forgó tartalom.
	 * Javaslat: A válaszban jelezzük a problémát, hogy a felhasználó korrigálhassa.
	 */
	const HTTP_CONFLICT = 409;

	/**
	 * A kért erőforrás megszűnt.
	 * Példa: xyz oldal törlésre került, ehelyett nézze meg ezt és ezt.
	 * Javaslat: Használata erősen javallott, a SE-k például figyelembe veszik,
	 * számukra többet mondó ez a válasz, mint egy 404-es hibakód.
	 */
	const HTTP_GONE = 410;
	const HTTP_LENGTH_REQUIRED = 411;
	const HTTP_PRECONDITION_FAILED = 412;
	const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
	const HTTP_REQUEST_URI_TOO_LONG = 414;
	const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
	const HTTP_REQUESTED_RANGE_NOT_STATISFIABLE = 416;
	const HTTP_EXPECTATION_FAILED = 417;

	/**
	 * Belső hiba jött létre a kiszolgáló oldalán.
	 * Példa: a rendszer kivétellel vagy fatális hibával tért vissza.
	 * Javaslat: Minden olyan esetben ajánlott a használata, amikor felhasználói
	 * szinten jelenítünk meg futást megszakító hibaüzenetet!
	 */
	const HTTP_INTERNAL_SERVER_ERROR = 500;
	const HTTP_NOT_IMPLEMENTED = 501;
	const HTTP_BAD_GATEWAY = 502;

	/**
	 * A szolgáltatás (vagy erőforrás) pillanatnyilag nem elérhető.
	 * Példa: karbantartás, stb.
	 * Javaslat: A Retry-After mezőben jelezhetjük, mikor válik újra elérhetővé
	 * a keresett erőforrás vagy szolgáltatás.
	 */
	const HTTP_SERVICE_UNAVAILABLE = 503;
	const HTTP_GATEWAY_TIMEOUT = 504;
	const HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;

	public $xName = 'Általános alkalmazás';

	public static $systemApplication = '';

	/**
	 * @var Task
	 */
	protected $task;

	protected $hash;

	protected $errorPageTpl = 'error.tpl';

	/**
	 * A központi rendszerállapot-kezelő dedikált példánya.
	 * @var x10_StateManager
	 */
	protected $stateMan;

	public function __construct(x10_Config $config=NULL)
	{
		parent::__construct($config);

		$errorPageTpl = $this->config->getConfigKey('errorPageTpl');
		if (isset($errorPageTpl)) $this->errorPageTpl = $errorPageTpl;

		if (empty(Task_Application::$systemApplication))
		{
			Task_Application::$systemApplication = get_class($this);

			// Az elsőként létrehozott alkalmazásnak nem lehet provider-e,
			// ami viszont mindenképp kell a megfelelő végrehajtáshoz.
			$this->provider = new Task_ItemProvider(
				new x10_Config(
					array(
						'itemName'		=> '',
						'itemClass'		=> '',
						'item'			=> $this,
						'parentTask'	=> NULL,
						'arguments'		=> $this->getUserArguments(),
						'parameters'	=> $this->parameters
					)
				)
			);
		}
	}

	/**
	 * Inicializálja az alkalmazást.
	 * Példányosítja az útvonalválasztókat, majd azok segítségével megpróbálja
	 * meghatározni az alapértelmezett taszkot. Ha az aktuális alkalmazás
	 * megegyezik a rendszer alapértelmezett alkalmazásával, beállítja a
	 * megfelelő rendszerállapotot (engine.init).
	 */
	public function init()
	{
		// State ellenőrzés (beágyazott alkalmazások esetén nem váltunk többször)
		if (Task_Application::$systemApplication == get_class($this))
		{
			$this->stateMan['engine.init']->get()->extraHash = str_replace(array('/', '?'), array('#', '!'), x10::$userQuery);
			$this->stateMan->setStateTo('engine.init');
		}

		// Útvonalválasztás...
		parent::init();
		$this->task = $this->itemObject;
	}

	/**
	 * Futtatja az alkalmazás egy meghatározott feladatát.
	 * Az elsőként létrejövő alkalmazás elindítja a munkafolyamatot (session) és
	 * megváltoztatja a rendszer futtatási állapotát (engine.run).
	 */
	public function run($task='')
	{
		if (Task_Application::$systemApplication == get_class($this))
		{
			@session_start();
			$this->stateMan['engine.run']->get()->extraHash = $this->createHashByRequest();
			$this->stateMan->setStateTo('engine.run');
		}
		if (isset($this->task))
		{
			$this->task->init();
			$result = $this->task->run();

			if (is_null($this->parent))
			{
				$this->display($result);
			}
			
			$this->task->done();
			
			if (!is_null($this->parent))
			{
				return $result;
			}
		}
	}

	/**
	 * Lezárja az alkalmazás futását.
	 * Az elsőként létrehozott alkalmazás megváltoztatja a rendszer futási
	 * állapotát is (engine.done).
	 */
	public function done()
	{
		if (Task_Application::$systemApplication == get_class($this))
		{
			$this->stateMan->setStateTo('engine.done');
		}
	}

	/**
	 * Hibaüzenetek megjelenítésére használatos metódus.
	 * A hibaüzenethez társítható HTTP hibakód, üzenet, és egyéb UI paraméterek,
	 * amelyek segítségével összetettebb riportok is létrehozhatóak a szabványos
	 * felület segítségével. A HTTP hibakódokat törekedjünk széleskörben
	 * használni!
	 * @param String $message
	 * @param Int $httpErrorCode
	 * @param Array $uiParams
	 */
	public function reportStatus($message, $httpErrorCode=FALSE, $uiParams=array())
	{
		if ($httpErrorCode !== FALSE) $this->reportStatusHTTP($message, $httpErrorCode);
		if (!isset($uiParams['tplFile'])) $uiParams['tplFile'] = $this->errorPageTpl;
		if (!isset($uiParams['errorMessage'])) $uiParams['errorMessage'] = $message;

		$uiProv = $this->getUserInterface($uiParams);
		header('Content-type: '.$uiProv->contentType);
		echo($uiProv);
	}

	/**
	 * HTTP hibakódok és hibaüzenet fejléc kiküldésére.
	 * @param String $message
	 * @param Int $httpErrorCode
	 */
	public function reportStatusHTTP($message, $httpErrorCode)
	{
		header('HTTP/1.1 '.$httpErrorCode.' '.$message);
	}

	/**
	 * Visszatér az (al)alkalmazás elemnevének megfelelő standard osztálynévvel.
	 * @param String $application
	 * @return String
	 */
	public function getStdAppClassName($application)
	{
		return $this->getStandardClassName('Application', $application);
	}

	/**
	 * Visszatér a taszk elemnevének megfelelő standard osztálynévvel.
	 * @param String $task
	 * @return String
	 */
	public function getStdTaskClassName($task)
	{
		return $this->getStandardClassName('Task', FALSE, $task);
	}

	/**
	 * Visszatér a taszk egy akciójának elemneve szerint alkotott standard
	 * osztály nevével.
	 * @param String $task
	 * @param String $action
	 * @return String
	 */
	public function getStdActionClassName($task, $action)
	{
		return $this->getStandardClassName('Action', FALSE, $task, $action);
	}

	/**
	 * A getStd...ClassName() metódusok által hívott függvény.
	 * @param String $forKey
	 * @param String $application
	 * @param String $task
	 * @param String $action
	 * @return String
	 */
	protected function getStandardClassName($forKey, $application=FALSE, $task=FALSE, $action=FALSE)
	{
		$format = $this->config->getConfigKey('namingStandards')->$forKey;
		$placeholders = array('[PARENT_APPCLS]');
		$values = array(get_class($this));
		if ($application !== FALSE)
		{
			$placeholders[] = '[APPNAME]';
			$values[] = $application;
		}
		if ($task !== FALSE)
		{
			$placeholders[] = '[TASKNAME]';
			$values[] = $task;
		}
		if ($action !== FALSE)
		{
			$placeholders[] = '[ACTIONNAME]';
			$values[] = $action;
		}
		$result = str_replace($placeholders, $values, $format);
		return $result;
	}

	protected function createHashByRequest()
	{
		return md5(x10::$userQuery);
	}
}

?>